"""
异常处理
"""

from functools import partial

from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from pydantic import (
    BaseModel,
    Field,
)
from starlette.requests import Request
from starlette.responses import (
    JSONResponse,
    Response,
)

from my_typing import *
from utils.exception_utils import BusinessError


class _ValidationErrorModel(BaseModel):
    """ Pydantic ValidationError """

    loc: list[str | int] = Field(..., title="异常位置字段信息")
    msg: str = Field(..., title="异常消息")
    type: str = Field(..., title="异常类型")
    ctx: dict = Field(default_factory=dict, title="异常上下文信息")  # 自定义异常不会填充该字段


def _get_error_loc_info(error: _ValidationErrorModel) -> str:
    """ 获取异常位置字段信息 """

    origin_loc: list[str | int] = error.loc

    if (
        origin_loc
        and origin_loc[0] in ("body", "query", "path")
    ):
        origin_loc = origin_loc[1::]

    loc_info: str = ".".join(map(str, origin_loc))

    return loc_info


async def validation_exception_handler(
    request: Request,
    exc: RequestValidationError,
    app: FastAPI,
) -> JSONResponse:
    """
    RequestValidationError（继承于 ValidationError） 异常处理
    """

    from components.response import ErrorResponse
    from components.route import get_route_path_2_model
    from utils.validator_utils.pydantic_x import (
        ErrorWrapper,
        pydantic_exception_handler,
    )

    route_path_2_model: dict[str, type[BaseModel]] = get_route_path_2_model(app)
    path: str = request.scope["route"].path
    model: type[BaseModel] | None = route_path_2_model.get(path)

    # 移除异常信息的前缀
    prefix_fields: TupleStr = ("body", "query", "path")

    for error in exc.errors():
        if error["loc"] and error["loc"][0] in prefix_fields:
            error["loc"] = error["loc"][1::]

    error_wrapper: ErrorWrapper = pydantic_exception_handler(exc, model)

    return ErrorResponse(error_wrapper.string_list)


def business_exception_handler(
    request: Request,  # noqa: ignore[unused param]
    exc: BusinessError,
) -> JSONResponse:
    """
    BusinessError（自定义异常）异常处理
    """

    from components.response import ErrorResponse

    return ErrorResponse(exc.errors)


def register_exception_handlers(app: FastAPI) -> None:
    """ 注册异常处理器 """

    exception_2_handler: dict[type[Exception], Callable[[Request, Exception], Response]] = {
        BusinessError: business_exception_handler,
        RequestValidationError: partial(validation_exception_handler, app=app),
    }

    for exc, handler in exception_2_handler.items():
        app.add_exception_handler(exc, handler)
